﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Obsidian.Components;

namespace Obsidian.GameObjects
{
    /// <summary>
    /// Base class for all entities in Obsidian scenes.
    /// </summary>
    public class GameObject
    {
        /// <summary>
        /// The name of the object.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The Transform attached to this GameObject. (null if there is none attached)
        /// </summary>
        public Transform Transform { get { return Components.GetComponent<Transform>() as Transform; } set { Components.Add(value); } }

        /// <summary>
        /// The Renderer attached to this GameObject (Read Only). (null if there is none attached)
        /// </summary>
        public Renderer Renderer { get { return Components.GetBaseComponent(typeof(Renderer)) as Renderer; } set { Components.Add(value); } }

        /// <summary>
        /// The Camera attached to this GameObject (Read Only). (null if there is none attached)
        /// </summary>
        public Camera Camera { get { return Components.GetBaseComponent(typeof(Camera)) as Camera; } set { Components.Add(value); } }

        /// <summary>
        /// The layer the game object is in. A layer is in the range [0...32]
        /// </summary>
        public int Layer { get; set; }

        /// <summary>
        /// The tag of this game object.
        /// </summary>
        public string Tag;

        public ComponentCollection Components;

        public GameObject Parent { get; internal set; }

        public GameObjectCollection Children { get; private set; }

        public GameObject(string name = "name")
        {
            Name = name;
            Children = new GameObjectCollection(this);
            Components = new ComponentCollection();
        }

        public GameObject(string name, params Type[] components)
        {
            foreach (Type type in components)
            {
                //this.AddComponent(type);
            }
        }

        #region Component Stuff

        public T AddComponent<T>() where T : Component
        {
            return (this.AddComponent(typeof(T)) as T);
        }

        public Component AddComponent(string className)
        {
            throw new NotImplementedException();
        }

        public Component AddComponent(Type componentType)
        {
            throw new NotImplementedException();
        }

        public T GetComponent<T>() where T : Component
        {
            return (this.GetComponent(typeof(T)) as T);
        }

        public Component GetComponent(Type type)
        {
            throw new NotImplementedException();
        }

        #endregion
        public void Initialize()
        {
            Transform.GameObject = this;
           
            if (Renderer != null)
                Renderer.GameObject = this;

            if (Camera != null)
                Camera.GameObject = this; 

            foreach (var component in Components)
            {
                component.GameObject = this;
                if (component.GetType().BaseType == typeof(Script))
                {
                    var script = component as Script;
                    script.Awake();
                }
            }

            foreach (var child in Children)
            {
                child.Initialize();
            }
        }

        public void Update()
        {
            scripts.Clear();

            foreach (var item in Components)
            {
                if (item.GetType().BaseType == typeof(Script))
                {
                    scripts.Add(item);
                }
            }

            foreach (var s in scripts)
            {
                var script = s as Script;
                script.Update();
            }
                
            // update kids
            foreach (var child in Children)
            {
                child.Update();
            }
        }

        static List<Component> scripts = new List<Component>(10);
    }
}
